Sandbox 与代码执行工程
5-8 第 4.3 节提到 Coding Agent 必须有沙箱,但讲得很浅。本篇深入:什么场景必须沙箱、各类方案对比、E2B/Modal 实战、Code Interpreter 工程。这是 2025-2026 年新基础设施层的核心。
学前说明
让 AI 跑代码是它真正"做事"的关键能力。但代码执行的危险性是 AI 工具集中最高的——一行 rm -rf / 就能毁机器,一个 curl ... | sh 就能装恶意软件。
所以任何允许 AI 跑代码的系统都必须有沙箱。问题是:
- Docker 够吗?容器逃逸风险有多大?
- 云端沙箱(E2B)vs 本地沙箱怎么选?
- 多租户怎么隔离?
- 文件、网络、资源怎么限制?
- 代码跑完结果怎么取回?
本篇回答这些。
学习目标
- 区分四种主流沙箱方案(容器、microVM、WASM、云端)
- 用 E2B / Modal / Daytona 在生产环境跑 AI 生成代码
- 实现 Code Interpreter 模式
- 设计文件、网络、资源的多层隔离
- 处理多语言运行时(Python / Node / Shell)
- 评估"沙箱逃逸"风险并设计纵深防御
与现有知识的衔接
- 5-8 第 4.3 节:沙箱概览(前置)
- 04 Lethal Trifecta:YOLO 模式必须沙箱
- 05 第五章:YOLO + Docker 的本地实现
- 07 Agentic Loop:沙箱让 loop 安全做暴力试错
第一章:为什么必须沙箱
1.1 AI 跑代码的真实风险
不只是"AI 写错代码"。是 AI 可能:
- 被注入指令(参考 04 Lethal Trifecta):网页里"忘了给 AI 看"的指令让它跑恶意代码
- 执行不受控的依赖:
pip install some-typo-squatting装了恶意包 - 资源耗尽:死循环、fork bomb、内存泄漏
- 数据外泄:跑代码读
.env,HTTP 发出去 - 横向攻击:用你的机器扫描内网、攻击其他服务
1.2 三个真实事件(2024-2025)
社区报告的真实事故(脱敏后):
事件 1:开发者用 Claude Code YOLO 模式让 AI 处理一个 GitHub PR,PR 里有恶意 markdown,AI "总结" 时实际跑了 curl 拉远端 shell 脚本,开发者 .ssh 目录被打包外发。
事件 2:某 AI 平台让 AI 跑 Python 数据分析,攻击者通过提交"数据"包含 __import__('os').system('...'),逃出 Python 限制。
事件 3:开发者本地跑 Coding Agent,AI 误执行 npm publish 把含密钥的包发了。
1.3 "我不会被攻击"的错觉
很多人觉得"我又不是公开服务"。但风险来自:
- 公开 GitHub 仓库的 issues / PRs
- 第三方 npm 包(你装的)
- 网页内容(AI 浏览)
- 你以为安全的 RAG 文档(员工写的备忘录可能被攻击者编辑)
只要 AI 接触任何外部内容,就有被注入的可能。
第二章:四类沙箱方案
2.1 方案对比
| 方案 | 隔离级别 | 启动速度 | 资源开销 | 代表 |
|---|---|---|---|---|
| 容器(Docker) | 中 | 1-3 秒 | 低 | Docker、Podman |
| microVM(Firecracker) | 高 | 100-500ms | 中 | E2B、AWS Fargate |
| WASM | 极高(语言级) | < 100ms | 极低 | Cloudflare Workers AI |
| 云端进程 | 高 | 取决于 provider | 由 provider 管理 | Modal、Daytona、Replicate |
2.2 容器(Docker)
适合:
- 本地开发
- 任务中等风险
- 你信任宿主内核
不适合:
- 处理任意攻击者代码
- 多租户公开服务
- 极敏感场景
关键配置:
docker run --rm \
--memory=512m \
--cpus=0.5 \
--pids-limit=100 \
--network=none \
--read-only \
--tmpfs /tmp:size=64m \
--user nobody \
--cap-drop=ALL \
--security-opt=no-new-privileges \
-v $(pwd)/input:/input:ro \
-v $(pwd)/output:/output:rw \
agent-runtime:latest
每个标志都重要:
--memory / --cpus / --pids-limit:资源限制--network=none:网络隔离(按需放开)--read-only:根文件系统只读--tmpfs /tmp:内存盘(重启即清空)--user nobody:非 root--cap-drop=ALL:移除所有 Linux capabilities--security-opt=no-new-privileges:防 setuid 提权
2.3 microVM(Firecracker)
AWS 在 2018 年开源的 microVM 技术,现在是 E2B、Modal、Fly.io 的底层。
优点:
- 启动 < 500ms(接近容器)
- 完整 VM 隔离(KVM 级别)
- 攻击面小(最小化的 VMM)
不适合自己跑:运维复杂,建议用 SaaS。
2.4 WASM 沙箱
把代码编译到 WebAssembly 跑,语言级隔离。
优点:
- 极快(启动 < 100ms)
- 极轻(开销小)
- 跨平台
限制:
- 不是所有代码都能编译到 WASM
- Python、Node 部分功能受限
- 不能做"真实 IO"(要 host bridge)
适合:
- 用户提交的小函数
- 不需要文件/网络的场景
- 极高并发
代表:Cloudflare Workers、Spin。
2.5 云端沙箱(E2B、Modal、Daytona)
把执行外包给 SaaS。
优点:
- 不用自己运维
- 现成的多语言支持
- 内置文件、网络、超时控制
- 通常更安全(专业团队)
缺点:
- 数据要传出去(合规问题)
- 按用量付费
- 网络延迟
适合:
- 大多数生产场景
- 不想自建基础设施
- 多租户
第三章:E2B 实战
E2B 是 2025 年最流行的 AI Sandbox SaaS。专为 AI Agent 设计。
3.1 基础用法
import { Sandbox } from '@e2b/code-interpreter';
const sandbox = await Sandbox.create();
// 跑 Python
const execution = await sandbox.runCode(`
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3]})
print(df.sum())
`);
console.log(execution.text); // a 6
// 文件操作
await sandbox.files.write('/tmp/data.csv', 'a,b\n1,2\n3,4');
const content = await sandbox.files.read('/tmp/data.csv');
// 跑 shell
const result = await sandbox.commands.run('ls -la /tmp');
// 销毁
await sandbox.kill();
3.2 关键特性
隔离:
- 每个 Sandbox 是独立的 microVM
- 默认 5 分钟无操作自动销毁
- 可手动 kill
多语言:
- Python(默认带 pandas、numpy 等)
- Node.js
- 任何能装的(apt-get / pip / npm)
网络:
- 默认有外网(按需限制)
- 可以代理流量
3.3 给 AI Agent 用的模式
// AI Agent 调用沙箱的标准模式
const tools = [
{
name: 'execute_python',
description: '在隔离沙箱中执行 Python 代码并返回输出',
input_schema: {
type: 'object',
properties: {
code: { type: 'string' }
}
}
}
];
async function executePython({ code }: { code: string }) {
// 复用沙箱(同一个 session 内)
if (!session.sandbox) {
session.sandbox = await Sandbox.create({ timeoutMs: 30 * 60 * 1000 });
}
const exec = await session.sandbox.runCode(code, { timeoutMs: 30_000 });
// 限制返回大小
const output = exec.text.slice(0, 50_000);
return {
stdout: output,
error: exec.error?.value,
images: exec.results.filter(r => r.png).map(r => r.png), // matplotlib 图
};
}
3.4 多文件项目
const sandbox = await Sandbox.create();
// 上传整个项目
await sandbox.files.write('/project/package.json', '...');
await sandbox.files.write('/project/src/index.ts', '...');
// ...更多文件
// 跑
await sandbox.commands.run('cd /project && npm install');
await sandbox.commands.run('cd /project && npm test');
// 取产物
const builtFile = await sandbox.files.read('/project/dist/bundle.js');
3.5 计费与限制
- 按秒计费
- 默认 1 vCPU / 512MB
- 可升到 8 vCPU / 16GB
- 长期跑(> 1 小时)成本要算
不适合:
- 跑训练任务(用 Modal)
- 极高并发(用 Cloudflare)
第四章:Modal 实战
Modal 适合更重的工作负载。
4.1 与 E2B 的区别
| 维度 | E2B | Modal |
|---|---|---|
| 定位 | AI 临时沙箱 | 通用 serverless |
| 启动速度 | < 500ms | 1-3 秒 |
| GPU 支持 | 有限 | 强(A100/H100) |
| 定价 | 按秒 | 按秒 + GPU 时间 |
| 适合 | 短任务、试错 | 训练、批处理、长任务 |
4.2 给 Agent 用的模式
import modal
app = modal.App("ai-sandbox")
@app.function(
image=modal.Image.debian_slim().pip_install("pandas", "numpy"),
timeout=300, # 5 分钟
cpu=1,
memory=512,
)
def execute_python(code: str) -> dict:
"""在隔离环境跑 Python"""
import sys, io, contextlib
stdout = io.StringIO()
stderr = io.StringIO()
try:
with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
exec(code, {})
return {
'stdout': stdout.getvalue(),
'stderr': stderr.getvalue(),
'success': True
}
except Exception as e:
return {
'stdout': stdout.getvalue(),
'stderr': str(e),
'success': False
}
# Agent tool 调用
@app.function()
async def agent_run_code(code: str):
return execute_python.remote(code)
4.3 长任务、GPU 任务
Modal 在这里有优势:
@app.function(
gpu="A100",
timeout=3600,
)
def fine_tune_model(dataset_url: str):
# AI Agent 派出来的微调任务
# 跑几小时也没问题
...
适合:让 Agent 自主决定"我来微调一个小模型解决这个问题"。
第五章:Code Interpreter 工程
OpenAI 和 Claude 都内置了 "Code Interpreter" 模式。它不是简单的代码执行——是Agent + 沙箱 + 持久会话的组合。
5.1 Code Interpreter 的特点
持久会话:
# 第一轮
> 加载这个 CSV
df = pd.read_csv('data.csv')
print(df.head())
# 第二轮(df 还在!)
> df 的均值是多少
print(df.mean())
# 第三轮
> 画个柱状图
df.plot.bar()
变量在会话内保留。这跟"每次跑独立代码"完全不同。
多模态输出:
- print 输出 → 文字
- matplotlib → 图片
- pandas DataFrame → 表格
- HTML → 交互组件
文件持久:
- 上传的文件在会话期间保留
- 生成的文件可以下载
5.2 自建 Code Interpreter
class CodeInterpreterSession {
private sandbox: E2BSandbox;
private session_id: string;
constructor(userId: string) {
this.session_id = `ci-${userId}-${Date.now()}`;
}
async ensureSandbox() {
if (!this.sandbox || !this.sandbox.isRunning()) {
this.sandbox = await Sandbox.create({
template: 'code-interpreter', // 预装 jupyter kernel
timeoutMs: 60 * 60 * 1000, // 1 小时
metadata: { session_id: this.session_id }
});
}
return this.sandbox;
}
async runCode(code: string): Promise<ExecutionResult> {
const sb = await this.ensureSandbox();
const exec = await sb.runCode(code);
// 提取多种输出
return {
stdout: exec.text,
images: this.extractImages(exec.results),
tables: this.extractTables(exec.results),
html: this.extractHtml(exec.results),
error: exec.error,
};
}
async uploadFile(name: string, content: Buffer) {
const sb = await this.ensureSandbox();
await sb.files.write(`/uploads/${name}`, content);
return `/uploads/${name}`;
}
async listFiles() {
const sb = await this.ensureSandbox();
const result = await sb.commands.run('ls /uploads /workspace');
return result.stdout.split('\n').filter(Boolean);
}
async cleanup() {
if (this.sandbox) await this.sandbox.kill();
}
}
5.3 与 LLM 集成
// LLM 通过 Tool Call 用 Code Interpreter
const tools = [
{
name: 'run_code',
description: '在持久 Python 会话中执行代码。变量和文件在会话内保留。',
input_schema: { code: { type: 'string' } }
},
{
name: 'list_files',
description: '列出沙箱中的文件',
input_schema: {}
}
];
async function handleConversation(userId: string, message: string) {
const ci = await getOrCreateSession(userId);
// 让 LLM 决定调用哪些 tool
const response = await llm.chat({
messages: [{ role: 'user', content: message }],
tools,
tool_handler: async (toolName, args) => {
if (toolName === 'run_code') {
return await ci.runCode(args.code);
}
// ...
}
});
return response;
}
第六章:多层隔离设计
单层隔离不够。生产系统需要纵深防御。
6.1 文件隔离
层级 1:进程级
- 用
--user nobody - 限制写入路径
层级 2:容器级
--read-only根文件系统--tmpfs临时盘
层级 3:宿主级
- 容器 mount 的目录最小化
- 没有
/host之类的逃出路径
6.2 网络隔离
# 三种网络模式
none: # 完全无网络
- 适合:纯计算任务
- 风险:极低
restricted: # 白名单
- 允许:pypi.org、npm registry、特定 API
- 拒绝:其他一切
- 适合:装包、调研
full: # 完整外网
- 适合:浏览器自动化
- 风险:高,必须配合其他隔离
实现:
# Docker 模式 1:完全隔离
docker run --network=none ...
# Docker 模式 2:白名单(用 iptables)
docker network create --internal restricted-net
# 在容器内通过代理访问白名单
6.3 资源隔离
docker run \
--memory=512m \
--memory-swap=512m \ # 不允许 swap 扩容
--cpus=0.5 \
--pids-limit=100 \ # 防 fork bomb
--ulimit nofile=1024:1024 \ # 文件句柄
--ulimit nproc=50:50 \ # 进程数
...
每个 limit 都防一种攻击。
6.4 时间隔离
- 单次执行:超时(30s)
- 整个会话:超时(1h)
- 闲置:自动销毁(5min)
async function safeExecute(code: string, timeoutMs = 30_000) {
return Promise.race([
sandbox.runCode(code),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('execution_timeout')), timeoutMs)
)
]);
}
6.5 多租户隔离
每个用户独立的沙箱实例。不要共享沙箱:
// ❌ 错误:用户共享沙箱
const sharedSandbox = await Sandbox.create();
async function run(userId: string, code: string) {
return sharedSandbox.runCode(code); // 用户 A 能看到 B 的变量
}
// ✅ 正确:每用户独立
const userSandboxes = new Map<string, Sandbox>();
async function run(userId: string, code: string) {
if (!userSandboxes.has(userId)) {
userSandboxes.set(userId, await Sandbox.create());
}
return userSandboxes.get(userId)!.runCode(code);
}
第七章:多语言运行时
7.1 Python 沙箱
最常用。E2B / Modal 默认支持。
关键考虑:
- 预装常用包(pandas、numpy、matplotlib、requests)
- 安装新包:
pip install(在沙箱内) - 内存:DataFrame 容易爆(限 4GB+)
7.2 Node.js 沙箱
const sandbox = await Sandbox.create({ template: 'node' });
await sandbox.commands.run('npm install lodash');
await sandbox.runCode(`
const _ = require('lodash');
console.log(_.sum([1, 2, 3]));
`, { language: 'javascript' });
注意:
- npm install 慢(用本地 cache 镜像可加速)
- 防止
child_process滥用
7.3 Shell 沙箱
最危险但也最强大。
// 白名单命令
const ALLOWED_COMMANDS = ['ls', 'cat', 'grep', 'find', 'git', 'pnpm', 'pytest'];
function isAllowed(command: string): boolean {
const cmd = command.split(/\s+/)[0];
return ALLOWED_COMMANDS.includes(cmd);
}
async function safeShell(command: string) {
if (!isAllowed(command)) {
throw new Error(`Command not allowed: ${command}`);
}
return await sandbox.commands.run(command);
}
或者完全开放但在严格沙箱里跑(取决于风险)。
7.4 跨语言统一接口
interface SandboxRuntime {
language: 'python' | 'node' | 'shell';
run(code: string, options?: RunOptions): Promise<Result>;
installPackage(name: string): Promise<void>;
uploadFile(path: string, content: Buffer): Promise<void>;
downloadFile(path: string): Promise<Buffer>;
cleanup(): Promise<void>;
}
class UnifiedSandbox implements SandboxRuntime {
// 用 E2B / Modal / 自建容器都实现这个接口
// Agent 不关心底层
}
第八章:沙箱逃逸风险
8.1 已知逃逸路径
容器逃逸(CVE 案例):
- runc 漏洞(2019、2024)
- 内核漏洞
- /sys/fs/cgroup 误配置
- 共享 namespace
防御:
- 用 microVM(更小攻击面)
- 用 gVisor 等 user-space kernel
- 限制 capabilities(
--cap-drop=ALL) - 用 seccomp 限制系统调用
8.2 应用层逃逸
不是逃出容器,是绕开应用层限制:
# 假设 Python 沙箱拦截了 import os
# 攻击者的绕过:
__builtins__.__import__('os').system('rm -rf /')
# 或者通过 dunder 方法
().__class__.__bases__[0].__subclasses__()
# 找到 subprocess.Popen 的引用
防御:
- 用真实进程隔离(不是 Python AST 限制)
- 不要试图用语言级 sandbox(容易被绕过)
8.3 侧信道攻击
- 缓存时序攻击
- 资源使用泄露信息
- ...
通常不在 AI 沙箱的威胁模型内(成本极高)。但如果你处理高敏感数据需要考虑。
8.4 纵深防御
不要靠单一防线:
威胁:恶意代码执行
↓
防线 1:用户输入校验(基础)
↓
防线 2:LLM 拒绝(Constitutional AI)
↓
防线 3:沙箱(容器 / VM)
↓
防线 4:网络白名单
↓
防线 5:资源限制
↓
防线 6:审计日志(事后追溯)
每层都有概率失效,多层叠加将组合失效概率压到极低。
第九章:实战案例
9.1 案例:数据分析 Agent
需求:用户上传 CSV,Agent 写代码分析,返回结果和图表。
架构:
用户上传 CSV
↓
后端:创建用户专属 E2B Sandbox
↓
LLM 看到任务,生成 Python 代码
↓
通过 Tool Call 调用 sandbox.runCode()
↓
拿到结果(文字 + 图片)
↓
LLM 综合给用户回答
关键决策:
- 沙箱生命周期 = 用户会话(关闭则销毁)
- 每个用户独立沙箱(不共享)
- 30 分钟自动 timeout
9.2 案例:CI 中的 AI 代码 Reviewer
需求:PR 提交后 AI 自动 review,包括"试着跑一下"。
架构:
# .github/workflows/ai-review.yml
- uses: actions/checkout@v4
- name: Create sandbox
uses: e2b-dev/action-create-sandbox@v1
with:
template: ${{ matrix.language }}
- name: Upload PR diff
run: e2b upload pr-diff
- name: Run AI review in sandbox
run: e2b exec "python /scripts/review.py"
- name: Post comments
run: ...
为什么用沙箱:PR 代码可能恶意(比如内部威胁、被入侵的 contributor)。直接在 CI runner 跑 PR 代码 = 灾难。
9.3 案例:Coding Agent 试错
需求:让 Claude Code 自由跑 npm install / pnpm test 但不能搞坏开发机。
方案:用 Anthropic 官方的 Dev Container。
# 项目根
.devcontainer/
├── devcontainer.json
├── Dockerfile
└── init-firewall.sh # 网络白名单
# 启动
code . # VS Code 自动 prompt 用 Dev Container
# 在 container 里跑 claude --dangerously-skip-permissions
容器内:
- 文件系统挂载到 /workspaces(项目目录)
- 网络限制到必要的域名
- 资源有限制
- 即使被注入也只能搞坏容器,不影响宿主机
第十章:踩坑总结
10.1 反模式
| 反模式 | 表现 | 后果 |
|---|---|---|
| 用 eval / vm.runInContext | "节省" 启动时间 | 没真隔离,能逃出 |
| 用 chroot 当沙箱 | 老办法 | 早就有逃逸方法 |
| 共享沙箱多用户 | 节省成本 | 数据泄露 |
| 不限网络 | 默认外网 | exfiltration 风险 |
| 不限超时 | 让它跑到完 | DoS、成本爆炸 |
| 不审计 | 不存执行日志 | 出事不可追溯 |
10.2 起步建议
不要一上来就 microVM。优先级:
- 本地 Coding Agent:Anthropic Dev Container(开箱即用)
- 生产 AI 应用:E2B(最简单的 SaaS 方案)
- 重负载:Modal(GPU、长任务)
- 极高并发:WASM(Cloudflare)
- 极敏感:自建 microVM(最后选项)
10.3 监控指标
| 指标 | 健康范围 |
|---|---|
| 平均沙箱寿命 | 1-30 分钟 |
| 沙箱启动失败率 | < 0.5% |
| 超时执行比例 | < 5% |
| 资源耗尽比例 | < 1% |
| 安全告警次数 | 接近 0 |
第十一章:未来方向
11.1 unikernel 沙箱
unikernel = 最小化内核 + 只跑一个应用。比 microVM 更轻、更安全。
代表:Nanos、IncludeOS。 2026 年开始有 AI 应用尝试。
11.2 Confidential Computing
CPU 级隔离(Intel SGX、AMD SEV):
- 即使云厂商也看不到内存
- 适合极敏感场景(医疗、金融)
成本高,2026 年开始普及。
11.3 AI 沙箱的标准化协议
类似 MCP 之于工具,可能出现"Sandbox Protocol":
- 统一的沙箱接口
- 跨厂商兼容
- AI 可以"自己选"沙箱
E2B、Modal 已经有讨论。
11.4 沙箱即服务的成本下降
2025 年 E2B 一个会话 $0.01-0.10/小时。2026 年随竞争加剧成本下降,可能进入"免费配额够用"阶段。
权威资料
- E2B 文档
- Modal 文档
- Daytona
- Firecracker
- Anthropic Claude Code Dev Container
- Designing agentic loops - YOLO mode (Simon Willison)
- gVisor
- 5-8 第 4.3 节(前置)
- 04 Lethal Trifecta
- 07 Agentic Loop 设计
核对日期:2026-06-12